package cn.jack.camera2encodeh264

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.os.Build
import android.os.Bundle
import android.util.Size
import android.view.TextureView
import android.view.ViewGroup
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import cn.jack.camera2encodeh264.ui.theme.Camera2EncodeH264Theme
import java.io.IOException
import java.nio.ByteBuffer

class MainActivity : ComponentActivity(), PreviewDataListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        checkPermission()

        setContent {
            Camera2EncodeH264Theme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    mTextureView = TextureView(this)
                    Greeting(this)
                }
            }
        }
    }

    private fun checkPermission(): Boolean {
        var allGranted = true
        allGranted = allGranted and (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(
                arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA
                ), 1
            )
        }
        return allGranted
    }

    private var nv21: ByteArray? = null
    private var nv21Rotated: ByteArray? = null
    private var nv12: ByteArray? = null
    private var mediaCodec: MediaCodec? = null

    @RequiresApi(Build.VERSION_CODES.R)
    override fun onPreview(
        y: ByteArray,
        u: ByteArray,
        v: ByteArray,
        previewSize: Size,
        stride: Int
    ) {
        //16.将Y U V转成 NV21
        if (nv21 == null) {
            nv21 = ByteArray(stride * previewSize.height * 3 / 2)
            nv21Rotated = ByteArray(stride * previewSize.height * 3 / 2)
        }
        if (mediaCodec == null) {
            //17.初始化mediacodec
            initCodec(previewSize)
        }
        //18.将YUV转成NV21
        ImageUtil.yuvToNv21(y, u, v, nv21, stride, previewSize.height)

        //19.将NV21旋转90°
        //对数据进行旋转   90度
        ImageUtil.nv21_rotate_to_90(nv21, nv21Rotated, stride, previewSize.height)
        //20.将NV21转成NV12
        val temp: ByteArray = ImageUtil.nv21toNV12(nv21Rotated, nv12)

        //输出成H264的码流

        val info = MediaCodec.BufferInfo()
        //解码 如果在指定的时间没有查询出可用的ByteBuffer，就返回-1
        val inIndex: Int = mediaCodec!!.dequeueInputBuffer(100000)
        if (inIndex >= 0) {
            //根据索引找到对应的ByteBuffer
            val byteBuffer: ByteBuffer? = mediaCodec!!.getInputBuffer(inIndex)
            //执行清空的操作
            byteBuffer!!.clear()
            //将temp中指定长度的内容放入到byteBuffer中
            byteBuffer.put(temp, 0, temp.size)

            //通知dsp芯片进行解码
            //参数1：要放入的输入缓冲区的索引
            //参数2：输入数据在输入缓冲区中的偏移量。通常，可以设置为 0，表示从输入数据的起始位置开始。
            //参数3：表示输入数据的大小，即要放入输入缓冲区的数据的字节数
            //参数4：表示输入数据的展示时间戳，以微秒为单位。对于视频来说，它通常是视频帧的显示时间；对于音频来说，它通常是音频样本的播放时间。
            //可以通过调整时间戳来实现对播放速度的控制
            //参数5：表示输入数据的标志位，用于指定一些特殊的处理要求。常见的标志位包括：
            //BUFFER_FLAG_KEY_FRAME: 表示关键帧。
            //BUFFER_FLAG_END_OF_STREAM: 表示输入流结束。
            //传入0，表示没有特殊的标志或配置，即不启用任何额外的特性。
            mediaCodec!!.queueInputBuffer(inIndex, 0, temp.size, 0, 0)
        }
        //（从输出缓冲区队列中获取可用的输出缓冲区索引）获取解码后的数据，以便应用程序可以进一步处理或渲染。
        //在参数2时间内没有获取到就返回-1     dequeueOutputBuffer方法的设计有些不太一样
        //参数1：一个 MediaCodec.BufferInfo 对象，用于存储输出缓冲区的相关信息，例如数据的大小、时间戳等。
        val outIndex: Int = mediaCodec!!.dequeueOutputBuffer(info, 100000)
        if (outIndex >= 0) {
            //获取对应的输出缓冲区，并从中读取解码后的数据
            val byteBuffer: ByteBuffer? = mediaCodec!!.getOutputBuffer(outIndex)
            val ba = ByteArray(byteBuffer!!.remaining())
            byteBuffer[ba]

            val path1 = "$filesDir/camera2Codec.txt"
            FileUtils.writeContent(ba,path1)
            val path2 = "$filesDir/camera2Codec.h264"
            FileUtils.writeBytes(ba,path2)

            //解码器的输出缓冲区中包含解码后的音视频数据。应用程序可以根据具体的需求，将这些数据用于播放、存储或其他后续处理。在处理完数据后，
            // 需要调用 releaseOutputBuffer 方法来释放输出缓冲区，使其变为可用状态供下一次使用。
            //render 参数表示是否渲染该输出缓冲区的数据。如果设置为 true，表示渲染；如果设置为 false，则不进行渲染。
            mediaCodec!!.releaseOutputBuffer(outIndex, false)
        }
    }

    private fun initCodec(size: Size) {
        try {
            //创建MediaCodec
            mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
            //mediaformat:用于指定媒体格式，包括音视频的编码格式、采样率、帧率等信息。它描述了输入数据的格式，以便编解码器能够正确地处理数据。
            val format = MediaFormat.createVideoFormat(
                MediaFormat.MIMETYPE_VIDEO_AVC,
                size.height,
                size.width
            )
            //设置帧率
            format.setInteger(
                MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible
            )
            format.setInteger(MediaFormat.KEY_FRAME_RATE, 15)
            format.setInteger(MediaFormat.KEY_BIT_RATE, 4000000)
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) //2s一个I帧
            //配置编解码器的参数
            //参数2：该参数用于指定一个Surface对象，如果你想在解码过程中将解码后的视频帧渲染到屏幕上，
            //可以通过这个Surface进行渲染。如果你只是进行音频解码，可以将此参数设置为null。
            //参数3：如果你需要对音视频进行数字版权管理（DRM）或其他加密操作，可以通过这个参数传递相应的MediaCrypto对象。如果不需要加密，可以将此参数设置为null。
            //参数4：将 flags 设置为 0 表明不启用任何附加的特性或标志，仅使用默认的配置。
            //通过设置 CONFIGURE_FLAG_ENCODE 来表示这是一个编码器，或者设置 CONFIGURE_FLAG_SECURE 来表示需要安全的解码。但如果没有这些特殊需求，通常可以将 flags 参数保持为 0。
            mediaCodec!!.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
            mediaCodec!!.start()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
}

@SuppressLint("StaticFieldLeak")
private lateinit var mTextureView: TextureView

@SuppressLint("StaticFieldLeak")
private lateinit var mCamera2Helper: Camera2Helper

@SuppressLint("StaticFieldLeak")
private lateinit var mContext: Context

@Composable
fun Greeting(previewDataListener: PreviewDataListener) {
    AndroidView(factory = { context: Context ->
        mContext = context
        mTextureView.apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
        }
    })

    fun initCamera2(previewDataListener: PreviewDataListener) {
        mCamera2Helper = Camera2Helper(mContext, mTextureView, previewDataListener)
        mCamera2Helper.start()
    }

    //1.设置监听
    mTextureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(
            surface: SurfaceTexture,
            width: Int,
            height: Int
        ) {
            //2.初始化摄像头
            initCamera2(previewDataListener);
        }

        override fun onSurfaceTextureSizeChanged(
            surface: SurfaceTexture,
            width: Int,
            height: Int
        ) {

        }

        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = false

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {

        }
    }
}